#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <mutex>
#include <set>
#include <cassert>

#include <jsoncpp/json/json.h>

#include "../common/log.hpp"
#include "../common/util.hpp"
#include "../common/httplib.h"
#include "./oj_model.hpp"
#include "./oj_view.hpp"

namespace ns_control
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_view;
    using namespace ns_model;

    class Machine
    {
    public:
        std::string _ip;             // 编译运行服务的ip
        uint16_t _port = 0;          // 编译运行服务的port
        uint64_t _load = 0;          // 编译运行服务的负载
        std::mutex *_pmtx = nullptr; // mutex禁止拷贝所以使用指针

        Machine()
        {
        }
        ~Machine()
        {
        }

        uint64_t GetLoad()
        {
            return _load;
        }

        // 减少主机负载
        void DecLoad()
        {
            std::unique_lock<std::mutex> ul(*_pmtx);

            --_load;
        }

        // 提升主机负载
        void IncLoad()
        {
            std::unique_lock<std::mutex> ul(*_pmtx);

            ++_load;
        }

    };

    const std::string service_machine = "./conf/service_machine.conf";

    // 负载均衡模块
    class LoadBalance
    {
    private:
        std::vector<Machine> _machines; // 编译运行服务
        std::set<int> _online;          // 记录在线的服务下标
        std::set<int> _offline;         // 记录离线的服务下标

        std::mutex _lock;

    public:
        LoadBalance()
        {
            assert(LoadConf(service_machine));
            LOG(LEVEL::INFO) << service_machine << ": 加载编译运行服务配置成功\n";
        }

        ~LoadBalance()
        {}

        bool LoadConf(const std::string &machine_conf)
        {
            std::ifstream fin(machine_conf); // 打开编译服务配置文件
            if (!fin.is_open())
            {
                return false;
            }

            std::string line;
            while (std::getline(fin, line))
            {
                std::vector<std::string> tokens;

                StringUtil::SplitString(line, &tokens, ":");

                if (tokens.size() != 2)
                {
                    LOG(LEVEL::WARNING) << line << ": 配置切分加载失败\n";
                    continue;
                }

                Machine machine;
                machine._ip = tokens[0];
                machine._port = std::atoi(tokens[1].c_str());
                machine._load = 0;
                machine._pmtx = new std::mutex{};

                _online.insert(_machines.size());
                _machines.push_back(machine);
            }

            fin.close();
            return true;
        }

        /***********************************
         * int *id: 输出型参数
         * Machine **m: 输出型参数
         * 选择负载最小的主机
         ************************************/
        bool SmartChoice(int *id, Machine **m)
        {
            // 负载均衡的算法
            // 1. 随机数+hash
            // 2. 轮询+hash

            std::unique_lock<std::mutex> ul(_lock);

            auto it = _online.begin();
            if (it == _online.end())
            {
                LOG(LEVEL::FATAL) << " 所有的后端编译主机已经离线\n";
                return false;
            }

            *id = *it;
            *m = &_machines[*it];
            uint64_t min_load = _machines[*it]._load;

            while (it != _online.end())
            {
                if (min_load > _machines[*it]._load)
                {
                    min_load = _machines[*it]._load;
                    *id = *it;
                    *m = &_machines[*it];
                }
                ++it;
            }

            return true;
        }

        // 离线主机
        void OfflineMachine(int which)
        {
            std::unique_lock<std::mutex> ul(_lock);

            _offline.erase(which);
            _online.insert(which);
        }

        // 上线主机
        void OnlineMachine()
        {
            std::unique_lock<std::mutex> ul(_lock);

            _online.insert(_offline.begin(),_offline.end());
            _offline.clear();

            LOG(LEVEL::INFO) << "所有的主机有上线啦!" << "\n";
        }

        void ShowMachines()
        {
            _lock.lock();
            std::cout << "当前在线主机列表: ";
            for(auto &id : _online)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            std::cout << "当前离线主机列表: ";
            for(auto &id : _offline)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            _lock.unlock();
        }

    };

    class Control
    {
    private:
        Model _model; // 提供后台数据
        View _view; // 提供html渲染功能
        LoadBalance _load_balance; // 核心负载均衡器
    public:
        Control() = default;
        ~Control() = default;

        bool AllQuestions(std::string *html)
        {
            bool ret = true;
            std::vector<Question> all;

            if (_model.GetAllQuestions(&all))
            {
                // 获取题目信息成功，将所有的题目数据构建成网页
                _view.AllExpandHtml(all, html);
            }
            else
            {
                *html = "获取题目失败, 形成题目列表失败";
                ret = false;
            }

            return ret;
        }

        bool OneQuestion(const std::string &number, std::string *html)
        {
            bool ret = true;

            Question q;

            if (_model.GetOneQuestion(number, &q))
            {
                _view.OneExpandHtml(q, html);
            }
            else
            {
                *html = "指定题目: " + number + " 不存在!";
                ret = false;
            }

            return ret;
        }

        void Judge(const std::string &number, const std::string &in_json, std::string *out_json)
        {
            // 0.同题目编号获取题目详细信息
            Question q;
            _model.GetOneQuestion(number,&q);
            // 1.通过in_json获取用户提交的代码
            Json::Value in_value;
            Json::Reader().parse(in_json,in_value);
            // 2.将用户提交的代码与该题目对应的测试代码合并
            Json::Value comp_run_value;
            comp_run_value["input"]=in_value["input"].asString();
            comp_run_value["code"]=in_value["code"].asString()+q.tail;
            comp_run_value["cpu_limit"]=q.cpu_limit;
            comp_run_value["mem_limit"]=q.mem_limit;
            std::string comp_run_str=Json::FastWriter().write(comp_run_value);
            // 3.负载均衡选择编译运行服务器
            while(true)
            {
                int id=0;
                Machine *machine=nullptr;

                if(!_load_balance.SmartChoice(&id,&machine))
                {
                    //continue;
                }

                // 4.将合并的代码形成新的in_json发送给编译运行服务器处理    
                httplib::Client client(machine->_ip,machine->_port);
                LOG(LEVEL::INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << machine->_ip << ":" << machine->_port << " 当前主机的负载是: " << machine->GetLoad() << "\n";
                
                machine->IncLoad();
                if(auto res = client.Post("/compile_and_run", comp_run_str, "application/json;charset=utf-8"))
                {
                    // 5. 将结果赋值给out_json
                    if(res->status == 200)
                    {
                        *out_json = res->body;
                        machine->DecLoad();

                        LOG(INFO) << "请求编译和运行服务成功..." << "\n";
                        break;
                    }
                    machine->DecLoad();
                }
                else
                {
                    //请求失败
                    LOG(LEVEL::ERROR) << " 当前请求的主机id: " << id << " 详情: " << machine->_ip << ":" << machine->_port << " 可能已经离线"<< "\n";
                    _load_balance.OfflineMachine(id);
                    _load_balance.ShowMachines(); // 仅仅是为了用来调试
                }
            }
        }
    };
}